---@diagnostic disable: lowercase-global, undefined-global, trailing-space --- 禁用特定的 Lua 诊断警告：小写全局变量、未定义全局变量、行尾空格

--- 设置全局环境元表，允许在当前环境 (env) 中直接访问 GLOBAL 表中的变量。
--- 当访问 env 中不存在的键时，会尝试从 GLOBAL 表中获取。
---@param env table 当前环境表。
---@return table 设置了元表的环境表。
GLOBAL.setmetatable(env,{__index=function(t,k) return GLOBAL.rawget(GLOBAL,k) end}) --- 设置元表，实现对 GLOBAL 的透明访问。当查找 env 中不存在的键 k 时，会调用 __index 元方法，该方法从 GLOBAL 表中原始获取 (rawget) 键 k 对应的值。

--- 定义 mod 的唯一标识符字符串。
local modid = 'ShadowFieldExciter'

--- 导入坐标处理相关的工具模块。
COORDS_ShadowFieldExciter = require('core_'..modid..'/utils/coords')
--- 导入提供语法糖（简化写法）的工具模块。
SUGAR_ShadowFieldExciter = require('core_'..modid..'/utils/sugar')

--- 将导入的坐标工具类注册到全局变量表中，使其可以在任何地方通过 `COORDS_ShadowFieldExciter` 访问。
rawset(GLOBAL,'COORDS_ShadowFieldExciter',COORDS_ShadowFieldExciter)
--- 将导入的语法糖工具类注册到全局变量表中，使其可以在任何地方通过 `SUGAR_ShadowFieldExciter` 访问。
rawset(GLOBAL,'SUGAR_ShadowFieldExciter',SUGAR_ShadowFieldExciter)

--- 定义一个包含所有需要被游戏加载的预制体 (Prefabs) 文件名的列表。
--- 预制体是游戏中对象（如物品、生物）的蓝图。
PrefabFiles = {
	'shadowfieldexciter',  --- 添加字符串 'shadowfieldexciter' 到 PrefabFiles 列表，代表主要物品的预制体。
    'shadowhandsfx',       --- 添加字符串 'shadowhandsfx' 到 PrefabFiles 列表，代表阴影手特效的预制体。
}

--- 定义一个包含所有 mod 需要的资源 (Assets) 的列表。
--- 资源包括图片、动画、音效等。
Assets = {
    -- 制作栏图标资源定义
    Asset("ATLAS", "images/inventoryimages/shadowfieldexciter.xml"),
    Asset("IMAGE", "images/inventoryimages/shadowfieldexciter.tex"),

    -- 自定义用户界面 (UI) 资源定义
    Asset("ATLAS","images/my_futuristic_ui/my_futuristic_ui.xml"),
    Asset("IMAGE","images/my_futuristic_ui/my_futuristic_ui.tex"),
    
    -- 动画资源定义
    Asset("ANIM","anim/sfe_interface.zip"),
    Asset("ANIM","anim/shadowhandsfx.zip"),
    Asset("ANIM","anim/preset_widget.zip"),

    -- 音效资源定义
    Asset("SOUNDPACKAGE","sound/Shadow_Field_Exciter_soundfx.fev"),
    Asset("SOUND","sound/Shadow_Field_Exciter_soundfx.fsb"),
}

---设置语言配置到TUNING表中
TUNING[string.upper('CONFIG_'..modid..'_LANG')] = GetModConfigData(modid..'_lang') --- 从 mod 配置中获取语言设置 (例如 'en' 或 'cn')，并将其存储到全局 TUNING 表中。键名被构造成 'CONFIG_SHADOWFIELDEXCITER_LANG' 的形式。 `string.upper` 将键名转换为大写。 `GetModConfigData` 是 Don't Starve Together API 提供的函数，用于读取 mod 的配置项。

---导入核心模块文件
modimport('scripts/core_'..modid..'/data/tuning.lua')
modimport('scripts/core_'..modid..'/utils/_register.lua')
modimport('scripts/core_'..modid..'/api/_register.lua')
modimport('scripts/core_'..modid..'/languages/'..TUNING[string.upper('CONFIG_'..modid..'_LANG')]..'.lua')  --- 根据之前设置的语言配置，动态加载对应的语言文件。例如，如果语言设置为 'cn'，则加载 'scripts/core_ShadowFieldExciter/languages/cn.lua'。

-- 导入调用器模块 (Callers) - 这些模块可能用于触发或调用核心功能
modimport('scripts/core_'..modid..'/callers/caller_container.lua')  --- 加载容器相关的调用器模块
modimport('scripts/core_'..modid..'/callers/caller_recipes.lua')  --- 加载配方相关的调用器模块

-- 导入钩子模块 (Hooks) - 这些模块通常用于修改或扩展游戏的原有行为
local files_hook = {
    'ChangeContainerWidget',  --- 代表修改容器界面的钩子。
}
for _,v in ipairs(files_hook) do
	modimport('scripts/core_'..modid..'/hooks/'..v..'.lua')  --- 动态加载列表中的每个钩子文件。
end

--#region 注册mod配置常量到局部变量 `db`，方便后续访问
local sfe_presetdata_utils = require('core_'..modid..'/utils/sfe_presetdata_utils') --- 导入预设数据工具模块，用于处理预设数据的保存和加载。8
local db = TUNING.MOD_ShadowFieldExciter --- 从全局 TUNING 表中获取名为 'MOD_ShadowFieldExciter' 的表（这个表可能在之前导入的 tuning.lua 文件中定义），并将其赋值给局部变量 `db`。

--设置当前数据版本
db.DataVersion = 1.1 --- 定义当前数据版本为 1.1。这个版本号可以用于数据兼容性检查或升级。
--设置物品搜索半径，从 mod 配置中读取并乘以 4 (游戏中距离单位通常需要调整)
db.FindItemsRadius = GetModConfigData(db.MODID..'_PickRadius') * 4
--设置光照半径，从 mod 配置中读取并乘以 4
db.LightRadius = GetModConfigData(db.MODID..'_LightRadius') * 4
--设置物品拾取间隔，从 mod 配置中读取
db.PickInterval = GetModConfigData(db.MODID..'_PickInterval')
--设置是否收取烹饪锅完成的料理，从 mod 配置中读取
db.HarvestCookpot = GetModConfigData(db.MODID..'_HarvestCookpot')
--设置预设表
sfe_presetdata_utils:Load(function(success, data)
    if success then
        db.PresetData = data or sfe_presetdata_utils:GetDefaultTemplate() --- 如果加载成功，则将数据赋值给 `db.PresetData`，否则使用默认模板。
    else
        db.PresetData = sfe_presetdata_utils:GetDefaultTemplate() --- 如果加载失败，则使用默认模板。
    end
end)
--#endregion

--#region 注册配方
-- local recipe = {
--     recipe_name = db.Prefab_ID, --建筑物名称
--     ingredients = { --配方
--         Ingredient("cutstone",2), 
--         Ingredient("nightmarefuel",2), 
--         Ingredient("transistor",2),
--     },
--     tech = {MAGIC = 0}, --所需科技无 ,TECH.LOST 表示需要蓝图才能解锁
--     --isOriginalItem = false, --是官方物品(官方物品严禁写atlas和image路径,因为是自动获取的),不写则为自定义物品
--     config = { --其他的一些配置,可不写
--         --制作出来的物品,不写则默认制作出来的预制物为食谱ID
--         product = db.Prefab_ID, 
--         placer = db.Prefab_ID..'_placer',
--         --xml路径,不写则默认路径为,'images/inventoryimages/'..product..'.xml' 或 'images/inventoryimages/'..recipe_name..'.xml'
--         atlas = 'images/inventoryimages/'..db.Prefab_ID..'.xml',
--         --图片名称,不写则默认名称为 product..'.tex' 或 recipe_name..'.tex'
--         --image = 'ShadowFieldExciter.tex',
--         --制作出的物品数量,不写则为1
--         --numtogive = 1,
--         --不需要解锁
--         --nounlock = true,--如果为 false，只能在对应的科技建筑旁制作。否则在初次解锁后，就可以在任意地点制作
--     },
--     filters = {'MAGIC','MODS'} --将物品添加到这些分类中
-- }
-- AddRecipe2(recipe.recipe_name,recipe.ingredients,recipe.tech,recipe.config,recipe.filters)
--#endregion

--#region 添加RPC处理函数

---注册RPC处理函数：将容器内物品返回给玩家。当客户端调用此 RPC 时，服务器会执行此函数。
---@param db.Prefab_ID string 预制体 ID，用于标识此 RPC 属于哪个预制体。通常在 `db` 表中定义。
---@param player EntityScript 触发此 RPC 的玩家实体对象。
---@param inst_guid string 目标容器实例的全局唯一标识符 (GUID)，以字符串形式传递。
AddModRPCHandler(db.Prefab_ID, "ReturnItemsToPlayer", function(player, inst_guid) --- 调用 `AddModRPCHandler` 函数注册一个 RPC。第一个参数是命名空间（通常是预制体 ID），第二个参数是 RPC 名称 ("ReturnItemsToPlayer")，第三个参数是处理该 RPC 的回调函数。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 从全局 `Ents` 表（存储所有实体）中查找容器实例。`tonumber` 将字符串 GUID 转换为数字索引。
    if inst and inst.components.container then --- 检查实例是否存在 (`inst`) 并且它是否拥有容器组件 (`inst.components.container`)。
        -- 获取容器的总槽位数
        local num_slots = inst.components.container.numslots or 0 --- 获取容器组件的 `numslots` 属性，如果不存在则默认为 0。
        
        -- 遍历所有槽位
        for slot = 1, num_slots do --- 使用 for 循环从 1 遍历到 `num_slots`。
            -- 检查槽位是否有物品
            if inst.components.container:GetItemInSlot(slot) then --- 调用容器组件的 `GetItemInSlot` 方法检查指定槽位 (`slot`) 是否有物品。
                -- 将物品移动到玩家背包
                inst.components.container:MoveItemFromAllOfSlot(slot, player, player) --- 调用容器组件的 `MoveItemFromAllOfSlot` 方法将指定槽位 (`slot`) 的所有物品移动到目标容器 (`player` 的背包，通常玩家实体本身也作为容器引用)。
            end
        end
    end
end)

---注册RPC处理函数：更新物品白名单。允许客户端更新服务器上特定容器的物品白名单。
---@param db.Prefab_ID string 预制体 ID。
---@param player EntityScript 触发 RPC 的玩家实体。
---@param inst_guid string 目标容器实例的 GUID。
---@param whitelist_json string 包含新白名单数据的 JSON 格式字符串。
AddModRPCHandler(db.Prefab_ID, "UpdateWhiteList", function(player, inst_guid, whitelist_json) --- 注册名为 "UpdateWhiteList" 的 RPC。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 查找容器实例。
    
    -- 验证玩家和容器有效性，并确保容器是正确的预制体类型
    if player and inst and inst.prefab == "shadowfieldexciter" then --- 检查玩家和实例是否存在，并且实例的预制体名称 (`inst.prefab`) 是否为 "shadowfieldexciter"。
        -- 解析JSON字符串为Lua表
        local whitelist = json.decode(whitelist_json) --- 使用 `json.decode` 函数（通常需要引入 JSON 处理库）将 JSON 字符串解析为 Lua 表。
        
        -- 验证解析结果是否为表，并更新白名单
        if type(whitelist) == "table" then --- 检查 `json.decode` 的返回值类型是否为 'table'。
            inst.ItemWhiteList = whitelist --- 将解析后的 `whitelist` 表赋值给容器实例的 `ItemWhiteList` 属性（这应该是 mod 自定义的属性）。
            -- 同步更新网络变量 (NetVar)，以便将更改广播给所有客户端
            inst.ItemWhiteListNet:set(whitelist_json) --- 调用网络变量 `ItemWhiteListNet` 的 `set` 方法，将原始 JSON 字符串传递给它，以同步状态。网络变量是 Don't Starve Together 用于同步数据的机制。
        end
    end
end)

---注册RPC处理函数：更新物品黑名单。允许客户端更新服务器上特定容器的物品黑名单。
---@param db.Prefab_ID string 预制体 ID。
---@param player EntityScript 触发 RPC 的玩家实体。
---@param inst_guid string 目标容器实例的 GUID。
---@param blacklist_json string 包含新黑名单数据的 JSON 格式字符串。
AddModRPCHandler(db.Prefab_ID, "UpdateBlackList", function(player, inst_guid, blacklist_json) --- 注册名为 "UpdateBlackList" 的 RPC。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 查找容器实例。
    
    -- 验证玩家和容器有效性，并确保容器是正确的预制体类型
    if player and inst and inst.prefab == "shadowfieldexciter" then --- 检查玩家、实例及预制体类型。
        -- 解析JSON字符串为Lua表
        local blacklist = json.decode(blacklist_json) --- 解析 JSON 黑名单数据。
        
        -- 验证解析结果是否为表，并更新黑名单
        if type(blacklist) == "table" then --- 检查解析结果类型。
            inst.ItemBlackList = blacklist --- 更新实例的 `ItemBlackList` 属性（mod 自定义）。
            -- 同步更新网络变量
            inst.ItemBlackListNet:set(blacklist_json) --- 通过网络变量 `ItemBlackListNet` 同步黑名单状态。
        end
    end
end)

---注册RPC处理函数：更新物品收集规则。允许客户端更新服务器上特定容器的收集规则。
---@param db.Prefab_ID string 预制体 ID。
---@param player EntityScript 触发 RPC 的玩家实体。
---@param inst_guid string 目标容器实例的 GUID。
---@param rules_json string 包含新收集规则数据的 JSON 格式字符串。
AddModRPCHandler(db.Prefab_ID, "UpdateCollectRule", function(player, inst_guid, rules_json) --- 注册名为 "UpdateCollectRule" 的 RPC。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 查找容器实例。
    
    -- 验证玩家和容器有效性，并确保容器是正确的预制体类型
    if player and inst and inst.prefab == "shadowfieldexciter" then --- 检查玩家、实例及预制体类型。
        -- 解析JSON字符串为Lua表
        local rules = json.decode(rules_json) --- 解析 JSON 收集规则数据。
        
        -- 验证解析结果是否为表，并更新收集规则
        if type(rules) == "table" then --- 检查解析结果类型。
            inst.CollectRule = rules --- 更新实例的 `CollectRule` 属性（mod 自定义）。
            -- 同步更新网络变量
            inst.CollectRuleNet:set(rules_json) --- 通过网络变量 `CollectRuleNet` 同步收集规则状态。
        end
    end
end)

---注册RPC处理函数：更新物品收集模式。允许客户端更新服务器上特定容器的收集模式。
---@param db.Prefab_ID string 预制体 ID。
---@param player EntityScript 触发 RPC 的玩家实体。
---@param inst_guid string 目标容器实例的 GUID。
---@param modes_json string 包含新收集规则数据的 JSON 格式字符串。
AddModRPCHandler(db.Prefab_ID, "UpdateCollectMode", function(player, inst_guid, modes_json) --- 注册名为 "UpdateCollectMode" 的 RPC。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 查找容器实例。
    
    -- 验证玩家和容器有效性，并确保容器是正确的预制体类型
    if player and inst and inst.prefab == "shadowfieldexciter" then --- 检查玩家、实例及预制体类型。
        -- 解析JSON字符串为Lua表
        local modes = json.decode(modes_json) --- 解析 JSON 收集规则数据。
        
        -- 验证解析结果是否为表，并更新收集规则
        if type(modes) == "table" then --- 检查解析结果类型。
            inst.CollectMode = modes --- 更新实例的 `CollectMode` 属性（mod 自定义）。
            -- 同步更新网络变量
            inst.CollectModeNet:set(modes_json) --- 通过网络变量 `CollectModeNet` 同步收集规则状态。
        end
    end
end)

---注册RPC处理函数：关闭容器界面。允许客户端请求服务器关闭特定容器的界面（通常在玩家远离容器时触发）。
---@param db.Prefab_ID string 预制体 ID。
---@param player EntityScript 触发 RPC 的玩家实体 (虽然在此函数中未使用，但通常是 RPC 处理函数的标准参数)。
---@param inst_guid string 目标容器实例的 GUID。
AddModRPCHandler(db.Prefab_ID, "CloseContainer", function(player, inst_guid) --- 注册名为 "CloseContainer" 的 RPC。
    -- 查找对应的容器实例
    local inst = Ents[tonumber(inst_guid)] --- 通过 GUID 查找容器实例。
    
    -- 验证容器和容器组件有效性
    if inst and inst.components and inst.components.container then --- 检查实例是否存在，并且它是否拥有组件表 (`inst.components`) 和容器组件 (`inst.components.container`)。
        -- 调用容器组件的关闭方法
        inst.components.container:Close() --- 调用容器组件的 `Close` 方法来关闭容器界面。
    end
end)
--#endregion -- 结束 RPC 处理函数代码块
